﻿/* QImageUtils.h
 * 
 * Copyright (C) 2020, 2021 Yuan LI
 * email:18069211@qq.com
 * https://blog.csdn.net/liyuanbhu
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */


#include "QImageUtils.h"
#include <QDebug>
QImage roi(const QImage &image, const QRect &rect)
{
    int x, y, width, height;

    if(rect.isNull())
    {
        x = 0;
        y = 0;
        width = image.width();
        height = image.height();
    }
    else
    {
        x = rect.x();
        y = rect.y();
        width = rect.width();
        height = rect.height();
        x = qBound(0, x, image.width());
        y = qBound(0, y, image.height());
        width = qBound(1, width, image.width() - x);
        height = qBound(1, height, image.height() - y);
    }
    QImage ret;
    const uchar * line = image.constScanLine(y);
    int bytesPerLine = image.bytesPerLine();
    QImage::Format format = image.format();
    switch (format)
    {
    case QImage::Format_Invalid:
        break;
    case QImage::Format_Grayscale8:
    case QImage::Format_Alpha8:
        line += x;
        ret = QImage(line, width, height, bytesPerLine, format);
        break;
    case QImage::Format_Indexed8:
        line += x;
        ret = QImage(line, width, height, bytesPerLine, format);
        ret.setColorTable(image.colorTable());
        break;
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
    case QImage::Format_RGBX8888:
    case QImage::Format_RGBA8888:
    case QImage::Format_RGBA8888_Premultiplied:
    case QImage::QImage::Format_BGR30:
    case QImage::Format_A2BGR30_Premultiplied:
    case QImage::Format_RGB30:
    case QImage::Format_A2RGB30_Premultiplied:
        line += x * 4;
        ret = QImage(line, width, height, bytesPerLine, format);
        break;
    case QImage::Format_RGB666:
    case QImage::Format_ARGB6666_Premultiplied:
    case QImage::Format_ARGB8555_Premultiplied:
    case QImage::Format_RGB888:
        line += x * 3;
        ret = QImage(line, width, height, bytesPerLine, format);
        break;
    case QImage::Format_RGB16:
    case QImage::Format_RGB555:
    case QImage::Format_RGB444:
    case QImage::Format_ARGB4444_Premultiplied:
        line += x * 2;
        ret = QImage(line, width, height, bytesPerLine, format);
        break;
    case QImage::Format_RGBX64:
    case QImage::Format_RGBA64:
    case QImage::Format_RGBA64_Premultiplied:
        line += x * 8;
        ret = QImage(line, width, height, bytesPerLine, format);
        break;
    default:
        break;
    }
    return ret;
}


void colorReduce(QImage &image, int div=64)
{
    if(image.isNull()) return;
    int nl = image.height(); // number of lines
    // total number of elements per line
    int nc = image.bytesPerLine();
    for (int j = 0; j < nl; j++)
    {
        // get the address of row j
        uchar* data = image.scanLine(j);
        for (int i = 0; i < nc; i++)
        {
            // process each pixel ---------------------
            data[i] = data[i] / div * div + div / 2;
            // end of pixel processing ----------------
        }
    } // end of line
}

QImage toGray(const QImage &image)
{
    if(image.format() == QImage::Format_Grayscale8 ||
            image.format() == QImage::Format_Indexed8 ||
            image.format() == QImage::Format_Alpha8)
    {
        return image;
    }
    int rows = image.height();
    int cols = image.width();
    QImage ret(image.size(), QImage::Format_Grayscale8);

    if(image.format() == QImage::Format_RGB888)
    {
        for(int i = 0; i < rows; i++)
        {
            uchar *line = ret.scanLine(i);
            const uchar * colorline = image.constScanLine(i);
            for(int j = 0; j < cols;  j++)
            {
                int r = colorline[3 * j + 0];
                int g = colorline[3 * j + 1];
                int b = colorline[3 * j + 2];
                //if(j == 0 ) qDebug() <<"r = " << r << ",g = " << g << ", b = " << b;
                line[j] = static_cast<uchar>(qGray(r, g, b));
            }
        }
        return ret;
    }
    if( image.format() == QImage::Format_RGB32 ||
            image.format() == QImage::Format_ARGB32 ||
            image.format() == QImage::Format_ARGB32_Premultiplied )
    {
        for(int i = 0; i < rows; i++)
        {
            uchar *line = ret.scanLine(i);
            const QRgb * colorline = reinterpret_cast<const QRgb*>(image.constScanLine(i));
            for(int j = 0; j < cols;  j++)
            {
                QRgb rgb = colorline[j];
                line[j] = static_cast<uchar>(qGray(rgb));
            }
        }
        return ret;
    }
    return ret;
}

QImage convertToGray(const QImage &image)
{
    return image.convertToFormat(QImage::Format_Grayscale8);
}

bool isGrayImage(const QImage &image)
{
    switch (image.format()) {
    case QImage::Format_Alpha8:
    case QImage::Format_Indexed8:
    case QImage::Format_Grayscale8:
    case QImage::Format_Grayscale16:
        return true;
    default:
        return false;
    }
    return false;
}

BrightnessMapper::BrightnessMapper()
{
    for(int i = 0; i < 256; i++)
    {
        m_red[i] = i;
        m_green[i] = i;
        m_blue[i] = i;
    }
}

BrightnessMapper::~BrightnessMapper()
{

}

void BrightnessMapper::updateBCG(double brightness, double contrast, double gamma)
{
    double x, y;
    for(int i = 0; i < 256; i ++)
    {
        x = i / 255.0;
        y = exp(log(x) * gamma);
        y = (y - 0.5) * contrast + 0.5 + brightness / 255;
        y = y * 255.0;
        m_red[i] = qBound(0.0, y, 255.0);
        m_green[i] = m_red[i];
        m_blue[i] = m_red[i];

    }
}

QImage BrightnessMapper::apply(const QImage &from)
{
    QImage to = from;
    apply(from, to);
    return to;
}

void BrightnessMapper::apply(const QImage &from, QImage &to)
{
    if(to.size() != from.size() || to.format()!= from.format())
    {
        to = from.copy();
    }
    int height = from.height();
    int width = from.width();
    switch(from.format())
    {
    case QImage::Format_Indexed8:
    case QImage::Format_Grayscale8:
    case QImage::Format_Alpha8:
        for(int i = 0; i < height; i++)
        {
            const uchar *pFrom = (const uchar *)from.constScanLine(i);
            uchar *pTo = (uchar *)to.scanLine(i);
            for(int j = 0; j < width; j++)
            {
                pTo[j] = m_red[pFrom[i]];
            }
        }
        break;

    case QImage::Format_RGB888:
        for(int i = 0; i < height; i++)
        {
            uchar * pTo = to.scanLine(i);
            const uchar * pFrom = from.constScanLine(i);
            for(int j = 0; j < width;  j++)
            {
                int r = pFrom[3 * j + 0];
                int g = pFrom[3 * j + 1];
                int b = pFrom[3 * j + 2];
                pTo[3 * j + 0] = m_red[r];
                pTo[3 * j + 1] = m_green[g];
                pTo[3 * j + 2] = m_blue[b];
            }
        }

    case QImage::Format_RGB32:
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
        for(int i = 0; i < height; i++)
        {
            const QRgb *pFrom = (const QRgb *)from.constScanLine(i);
            QRgb *pTo = (QRgb *)to.scanLine(i);
            for(int j = 0; j < width; j++)
            {
                int r, g, b;
                r = qRed(pFrom[j]);
                g = qGreen(pFrom[j]);
                b = qBlue(pFrom[j]);
                r = m_red[r];
                g = m_green[g];
                b = m_blue[b];
                pTo[j] = qRgb(r, g, b);
            }
        }
        break;
    }
}

void BrightnessMapper::setRedMap(unsigned char red[])
{
    for(int i = 0; i < 256; i++)
    {
        m_red[i] = red[i];
    }
}

void BrightnessMapper::setGreenMap(unsigned char green[])
{
    for(int i = 0; i < 256; i++)
    {
        m_green[i] = green[i];
    }
}

void BrightnessMapper::setBlueMap(unsigned char blue[])
{
    for(int i = 0; i < 256; i++)
    {
        m_blue[i] = blue[i];
    }
}

void BrightnessMapper::setMap(unsigned char map[])
{
    for(int i = 0; i < 256; i++)
    {
        m_red[i] = map[i];
        m_green[i] = map[i];
        m_blue[i] = map[i];
    }
}

void BrightnessMapper::setColorMap(unsigned char red[], unsigned char green[], unsigned char blue[])
{
    for(int i = 0; i < 256; i++)
    {
        m_red[i] = red[i];
        m_green[i] = green[i];
        m_blue[i] = blue[i];
    }
}

void BrightnessMapper::redMap(double red[256])
{
    for(int i = 0; i < 256; i++)
    {
        red[i] = m_red[i];
    }
}

void BrightnessMapper::greenMap(double green[256])
{
    for(int i = 0; i < 256; i++)
    {
        green[i] = m_green[i];
    }
}

void BrightnessMapper::blueMap(double blue[256])
{
    for(int i = 0; i < 256; i++)
    {
        blue[i] = m_blue[i];
    }
}


